home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1998 November / Freeware November 1998.img / dist / fw_UDELxntp.idb / usr / freeware / src / xntp-3.4o-export / xntpd / ntp_leap.c.z / ntp_leap.c
C/C++ Source or Header  |  1998-01-21  |  7KB  |  316 lines

  1. /*
  2.  * ntp_leap - maintain leap bits and take action when a leap occurs
  3.  */
  4. #include <stdio.h>
  5.  
  6. #include "ntpd.h"
  7. #include "ntp_stdlib.h"
  8.  
  9. /*
  10.  * This module is devoted to maintaining the leap bits and taking
  11.  * action when a leap second occurs.  It probably has bugs since
  12.  * a leap second has never occurred to excercise the code.
  13.  *
  14.  * The code does two things when a leap second occurs.  It first
  15.  * steps the clock one second in the appropriate direction.  It
  16.  * then informs the reference clock code, if compiled in, that the
  17.  * leap second has occurred so that any clocks which need to disable
  18.  * themselves can do so.  This is done within the first few seconds
  19.  * after midnight, UTC.
  20.  *
  21.  * The code maintains two variables which may be written externally,
  22.  * leap_warning and leap_indicator.  Leap_warning can be written
  23.  * any time in the month preceeding a leap second.  24 hours before
  24.  * the leap is to occur, leap_warning's contents are copied to
  25.  * leap_indicator.  The latter is used by reference clocks to set
  26.  * their leap bits.
  27.  *
  28.  * The module normally maintains a timer which is arranged to expire
  29.  * just after 0000Z one day before the leap.  On the day a leap might
  30.  * occur the interrupt is aimed at 2200Z and every 5 minutes thereafter
  31.  * until 1200Z to see if the leap bits appear.
  32.  */
  33.  
  34. /*
  35.  * The leap indicator and leap warning flags.  Set by control messages
  36.  */
  37. u_char leap_indicator;
  38. u_char leap_warning;
  39. u_char leap_mask;        /* set on day before a potential leap */
  40. /*
  41.  * Timer.  The timer code imports this so it can call us prior to
  42.  * calling out any pending transmits.
  43.  */
  44. u_long leap_timer;
  45.  
  46. /*
  47.  * We don't want to do anything drastic if the leap function is handled
  48.  * by the kernel.
  49.  */
  50. extern int pll_control;        /* set nonzero if kernel pll in uss */
  51.  
  52. /*
  53.  * Internal leap bits.  If we see leap bits set during the last
  54.  * hour we set these.
  55.  */
  56. u_char leapbits;
  57.  
  58. /*
  59.  * Constants.
  60.  */
  61. #define    OKAYTOSETWARNING    (31*24*60*60)
  62. #define    DAYBEFORE        (24*60*60)
  63. #define    TWOHOURSBEFORE        (2*60*60)
  64. #define    FIVEMINUTES        (5*60)
  65. #define    ONEMINUTE        (60)
  66.  
  67. /*
  68.  * Imported from the timer module.
  69.  */
  70. extern u_long current_time;
  71.  
  72.  
  73. /*
  74.  * Some statistics counters
  75.  */
  76. u_long leap_processcalls;    /* calls to leap_process */
  77. u_long leap_notclose;        /* leap found to be a long time from now */
  78. u_long leap_monthofleap;    /* in the month of a leap */
  79. u_long leap_dayofleap;        /* This is the day of the leap */
  80. u_long leap_hoursfromleap;    /* only 2 hours from leap */
  81. u_long leap_happened;        /* leap process saw the leap */
  82.  
  83. /*
  84.  * Imported from the main module
  85.  */
  86. extern int debug;
  87.  
  88.  
  89. static void    setnexttimeout    P((u_long));
  90.  
  91. /*
  92.  * init_leap - initialize the leap module's data.
  93.  */
  94. void
  95. init_leap()
  96. {
  97.     /*
  98.      * Zero the indicators.  Schedule an event for just after
  99.      * initialization so we can sort things out.
  100.      */
  101.     leap_indicator = leap_warning = leap_mask = 0;
  102.     leap_timer = 1<<EVENT_TIMEOUT;
  103.     leapbits = 0;
  104.  
  105.     leap_processcalls = leap_notclose = 0;
  106.     leap_monthofleap = leap_dayofleap = 0;
  107.     leap_hoursfromleap = leap_happened = 0;
  108. }
  109.  
  110.  
  111. /*
  112.  * leap_process - process a leap event expiry and/or a system time step
  113.  */
  114. void
  115. leap_process()
  116. {
  117.     u_long leapnext;
  118.     u_long leaplast;
  119.     l_fp ts;
  120.     u_char bits;
  121.     extern u_char sys_leap;
  122.  
  123.     leap_processcalls++;
  124.     get_systime(&ts);
  125.     calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext);
  126.  
  127.     /*
  128.      * Figure out what to do based on how long to the next leap.
  129.      */
  130.     if (leapnext > OKAYTOSETWARNING) {
  131.         if (leaplast < ONEMINUTE) {
  132.             /*
  133.              * The golden moment!  See if there's anything
  134.              * to do.
  135.              */
  136.             leap_happened++;
  137.             bits = 0;
  138.             leap_mask = 0;
  139.             if (leap_indicator != 0)
  140.                 bits = leap_indicator;
  141.             else if (leapbits != 0)
  142.                 bits = leapbits;
  143.             
  144.             if (bits != 0 && !pll_control) {
  145.                 l_fp tmp;
  146.  
  147.                 /*
  148.                  * Step the clock 1 second in the proper
  149.                  * direction.
  150.                  */
  151.                 if (bits == LEAP_DELSECOND)
  152.                     tmp.l_i = 1;
  153.                 else
  154.                     tmp.l_i = -1;
  155.                 tmp.l_uf = 0;
  156.  
  157.                 step_systime(&tmp);
  158. #ifdef SLEWALWAYS
  159.                 syslog(LOG_NOTICE,
  160.             "leap second occurred, slewed time %s 1 second",
  161.                     tmp.l_i > 0 ? "forward" : "back");
  162. #else
  163.                 syslog(LOG_NOTICE,
  164.             "leap second occurred, stepped time %s 1 second",
  165.                     tmp.l_i > 0 ? "forward" : "back");
  166. #endif
  167.             }
  168.         } else {
  169.             leap_notclose++;
  170.         }
  171.         leap_warning = 0;
  172.     } else {
  173.         if (leapnext > DAYBEFORE)
  174.             leap_monthofleap++;
  175.         else if (leapnext > TWOHOURSBEFORE)
  176.             leap_dayofleap++;
  177.         else
  178.             leap_hoursfromleap++;
  179.     }
  180.  
  181.     if (leapnext > DAYBEFORE) {
  182.         leap_indicator = 0;
  183.         leapbits = 0;
  184.         /*
  185.          * Berkeley's setitimer call does result in alarm
  186.          * signal drift despite rumours to the contrary.
  187.          * Schedule an event no more than 24 hours into
  188.          * the future to allow the event time to be
  189.          * recomputed.
  190.          */
  191.         if ((leapnext - DAYBEFORE) >= DAYBEFORE)
  192.             setnexttimeout((u_long)DAYBEFORE);
  193.         else
  194.             setnexttimeout(leapnext - DAYBEFORE);
  195.         return;
  196.     }
  197.  
  198.     /*
  199.      * Here we're in the day of the leap.  Set the leap indicator
  200.      * bits from the warning, if necessary.
  201.      */
  202.     if (leap_indicator == 0 && leap_warning != 0)
  203.         leap_indicator = leap_warning;
  204.     leap_mask = LEAP_NOTINSYNC;
  205.     if (leapnext > TWOHOURSBEFORE) {
  206.         leapbits = 0;
  207.         setnexttimeout(leapnext - TWOHOURSBEFORE);
  208.         return;
  209.     }
  210.  
  211.     /*
  212.      * Here we're in the final 2 hours.  If sys_leap is set, set
  213.      * leapbits to it.
  214.      */
  215.     if (sys_leap == LEAP_ADDSECOND || sys_leap == LEAP_DELSECOND)
  216.         leapbits = sys_leap;
  217.     setnexttimeout((leapnext > FIVEMINUTES) ? FIVEMINUTES : leapnext);
  218. }
  219.  
  220.  
  221. /*
  222.  * setnexttimeout - set the next leap alarm
  223.  */
  224. static void
  225. setnexttimeout(secs)
  226.     u_long secs;
  227. {
  228.     /*
  229.      * We try to aim the time out at between 1 and 1+(1<<EVENT_TIMEOUT)
  230.      * seconds after the desired time.
  231.      */
  232.     leap_timer = (secs + 1 + (1<<EVENT_TIMEOUT) + current_time)
  233.         & ~((1<<EVENT_TIMEOUT)-1);
  234. }
  235.  
  236.  
  237. /*
  238.  * leap_setleap - set leap_indicator and/or leap_warning.  Return failure
  239.  *          if we don't want to do it.
  240.  */
  241. int
  242. leap_setleap(indicator, warning)
  243.     int indicator;
  244.     int warning;
  245. {
  246.     u_long leapnext;
  247.     u_long leaplast;
  248.     l_fp ts;
  249.     int i;
  250.  
  251.     get_systime(&ts);
  252.     calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext);
  253.  
  254.     i = 0;
  255.     if (warning != ~0)
  256.         if (leapnext > OKAYTOSETWARNING)
  257.             i = 1;
  258.  
  259.     if (indicator != ~0)
  260.         if (leapnext > DAYBEFORE)
  261.             i = 1;
  262.     
  263.     if (i) {
  264.         syslog(LOG_ERR,
  265.             "attempt to set leap bits at unlikely time of month");
  266.         return 0;
  267.     }
  268.  
  269.     if (warning != ~0)
  270.         leap_warning = warning;
  271.  
  272.     if (indicator != ~0) {
  273.         if (indicator == LEAP_NOWARNING) {
  274.             leap_warning = LEAP_NOWARNING;
  275.         }
  276.         leap_indicator = indicator;
  277.     }
  278.     return 1;
  279. }
  280.  
  281. /*
  282.  * leap_actual
  283.  *
  284.  * calculate leap value - pass arg through of no local
  285.  * configuration. Otherwise ise local configuration
  286.  * (only used to cope with broken time servers and
  287.  * broken refclocks)
  288.  *
  289.  * Mapping of leap_indicator:
  290.  *    LEAP_NOWARNING
  291.  *        pass peer value to sys_leap - usual operation
  292.  *    LEAP_ADD/DEL_SECOND
  293.  *        pass  LEAP_ADD/DEL_SECOND to sys_leap
  294.  *    LEAP_NOTINSYNC
  295.  *        pass LEAP_NOWARNING to sys_leap - effectively ignores leap
  296.  */
  297. /* there seems to be a bug in the IRIX 4 compiler which prevents
  298.    u_char from beeing used in prototyped functions
  299.    AIX also suffers from this.
  300.    So give up and define it terms of int.
  301. */
  302. int
  303. leap_actual(l)
  304.     int l ;
  305. {
  306.     if (leap_indicator != LEAP_NOWARNING) {
  307.         if (leap_indicator == LEAP_NOTINSYNC)
  308.             return LEAP_NOWARNING;
  309.         else
  310.             return leap_indicator;
  311.     } else {
  312.         return l;
  313.     }
  314. }
  315.  
  316.